﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using VisualEsse;
using VisualEsse.DataEngine;
using System.Windows.Media.Imaging;
using System.Windows;
using MultiViewer.Display;

namespace MultiViewer
{
    public abstract class TiledEarthImage : TiledImage, IBoundToEarthImage, IDisposable
    {
        private ICache cache;

        private string mode = "h";

        public TiledEarthImage()
        {
            tileSize = new Size(256, 256);
            minLevel = 1;
            maxLevel = 22; // Max. level of details supported by Virtual Earth
            entireImage = new Rect(-180, -87.5, 180, 87.5);
            aspectRatio = 175 / 360.0;

  
        }

        public ICache Cache
        {
            get
            {
                if (cache == null)
                {
                    const int MaxCacheSize = 100000000; // About 100 Mbytes
                    string cachePath = Path.Combine(
                        Path.GetDirectoryName(Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName), UniqueCacheKey + "Cache");
                    cache = new XmlBasedCache(cachePath, MaxCacheSize);
                }
                return cache;
            }
        }

        public abstract string UniqueCacheKey { get; }

        public string Mode
        {
            get { return mode; }
            set { mode = value; }
        }

        public override BitmapImage GetTile(int row, int col, int level)
        {
            ICache cache = Cache;
            if (row < 0 || row >= (1 << level) ||
                col < 0 || col >= (1 << level) ||
                level < minLevel || level > maxLevel) // Return empty bitmap for out of range values 
                return new BitmapImage();//(tileSize.Width, tileSize.Height);
            string quadKey = TileToQuadKey(col, (1 << level) - row - 1, level);
            CacheKey cacheKey = new CacheKey(String.Concat(UniqueCacheKey, quadKey, mode));

            try
            {
                Stream imageStream = cache.OpenFile(cacheKey, FileAccess.Read);
                if (imageStream != null) // File is in cache
                {
                    try
                    {
                        return BitmapFromStream(imageStream);
                    }
                    finally
                    {
                        imageStream.Close();
                    }
                    // TODO: invalidate cache item in case of exception
                }
                else
                {
                    WebClient webClient = new WebClient();
                    byte[] data = webClient.DownloadData(GetUrl(quadKey));
                    Stream cacheStream = cache.OpenOrCreateFile(cacheKey, FileAccess.Write);
                    MemoryStream ms = new MemoryStream(data);
                    BitmapImage tile = BitmapFromStream(ms);
                    ms.Position = 0;
                    Utils.CopyStream(cacheStream, ms);
                    cacheStream.Close();
                    ms.Close();
                    return tile;
                }
            }
            catch (Exception exc)
            {
                return new BitmapImage(/*tileSize.Width, tileSize.Height*/);
                // TODO: return predefined bitmap with error picture
            }
        }

        public void StoreCache()
        {
            if(cache != null) 
            {
                XmlBasedCache xc = cache as XmlBasedCache;
                xc.StoreXml();
            }
        }

        private BitmapImage BitmapFromStream(Stream stream)
        {
            BitmapImage bmp = new BitmapImage();

            bmp.BeginInit();
            bmp.StreamSource = stream;
            bmp.CacheOption = BitmapCacheOption.OnLoad;
            bmp.EndInit();

            return bmp;
        }

        public override void Dispose()
        {
            if (cache != null)
            {
                cache.Dispose();
                cache = null;
            }
        }

        private static string TileToQuadKey(int tx, int ty, int zl)
        {
            string quad = "";
            for (int i = zl; i > 0; i--)
            {
                int mask = 1 << (i - 1);
                int cell = 0;
                if ((tx & mask) != 0)
                {
                    cell++;
                }
                if ((ty & mask) != 0)
                {
                    cell += 2;
                }
                quad += cell;
            }
            return quad;
        }

        protected abstract string GetUrl(string quadKey);

        #region IBoundToEarthImage Members

        public VisualEsse.DataEngine.Types.GeoRect GetTileRect(int row, int col, int level)
        {
            float lon = -180 + (float)col / (float)(1 << level) * 360;

            int y = (int)(row * 256);
            float lat2 = CoordinateConverter.YToLatitudeAtZoom(y, level);

            float lon2 =  -180 + (float)(1 + col) / (float)(1 << level) * 360;
            float lat = CoordinateConverter.YToLatitudeAtZoom((row + 1) * 256, level);

            return new VisualEsse.DataEngine.Types.GeoRect(lon, lon2, lat, lat2);
        }

        public Projection Projection
        {
            get { return Projection.Mercator; }
        }

        #endregion
    }

    public class VirtualEarthImage : TiledEarthImage
    {
        protected override string GetUrl(string quadKey)
        {
            string mapType = Mode; // or "a" for Aerial
            string mapExtension = ".png";

            //TODO what is the ?g= hanging off the end 1 or 15?
            // -- comment above was left by the article author. funny enough to keep it
            string url = String.Concat(new object[] { "http://", mapType, quadKey[quadKey.Length - 1], ".ortho.tiles.virtualearth.net/tiles/", mapType, quadKey, mapExtension, "?g=", 1 });
            return url;
        }

        public override string UniqueCacheKey
        {
            get { return "VE"; }
        }
    }

    public class CustomTiledImage : TiledEarthImage
    {
        private string uniqueId;
        private string urlTemplate;

        public CustomTiledImage(string uniqueId, string urlTemplate)
        {
            this.uniqueId = uniqueId;
            this.urlTemplate = urlTemplate;
        }

        protected override string GetUrl(string quadKey)
        {
            string url = urlTemplate.Replace("{0}",quadKey);
            // url = String.Format("http://arca7.wdcb.ru/WMSTiles/tiles/10/{0}.png", quadKey);
            return url;
        }

        public override string UniqueCacheKey
        {
            get { return "Custom" + uniqueId; }
        }
    }
}